How To Write An Award–Winning Scripts
AppleScript Part 4
By Stephen Harris
Welcome to the fourth part of 1984-Online’s series on AppleScript, the Mac’s best kept secret. Each part builds on the last, so if you missed any of the last three then they can be found in Issues 2, 3 and 4 of 1984-Online Magazine at http://www.1984-online.com. There is a glossary (“Techno-babble”) at the end of this article that explains some of the more technical terms used this month.

Scripting The Finder
Probably the most useful scriptable application on your Mac, and one that every user with System 7.5 or greater can use is the Finder. The Finder is not only scriptable but recordable, which makes life even easier. We all, at some point in our Mac-using lives, find ourselves performing mundane and repetitive actions which are ideal candidates for automation by AppleScript.

You alone know your needs, but I shall try to cover some of the more useful tasks here, with some handy hints thrown in for good measure.

Finder Dictionary
Dictionaries list all the AppleScript commands that an application can use, with information on how to use them. The application’s dictionary is most commonly held in the application itself. Let’s open the Finder’s dictionary now.

•
Open the Script Editor
•
Choose Open Dictionary from the File menu.
•
Choose ‘Finder’ in the System Folder, and click Open.

Now you have the Finder’s dictionary open. On the left hand side of the window you can see all the commands grouped into suites. If you click on the suites or any of the items in the list, the right hand side of the window shows its description.

Setup For This Activity
To do this activity, I’ve made the initial data setup easy for you. 

•
Choose New from the File menu in the Script Editor, cut and paste the following script and run it:

tell application "Finder"

activate

make new folder at desktop with properties name:"Original Folder"

make new folder at desktop with properties name:"Backup Folder"

make new folder at folder "Original Folder" of desktop with properties ¬ 

name:"Important Folder"
end tell

This has created two folders on your desktop, one’s called “Original Folder” and the other “Backup Folder”. Inside “Original Folder” is another folder called “Important Folder” and we’re going to copy this from one folder to another. 

Clearing Out Rubbish
Example: Let’s say we’ve recorded the following script to copy the “Important Folder” from the “Original Folder” to the “Backup Folder”:

tell application "Finder"

activate

select folder "Original Folder"

open selection

select folder "Important Folder" of folder "Original Folder"

copy selection to folder "Backup Folder"

close container window of folder "Original Folder"
end tell

As you can see, the script contains a lot of rubbish about selecting items and opening folders. When the script is run, all this activity not only wastes time, but is very disruptive too. The script can be edited down to this:

tell application "Finder"

activate

copy folder "Important Folder" of folder ¬

"Original Folder" to folder "Backup Folder"
end tell

The script would serve its purpose: to make a copy of the file ‘Customers 2’ in a specific folder. 

However, the script assumes that the operation is always going to be successful, which it isn’t. The first time it’s run a copy will be made, but the second time it will fail. This is because there will already be an item of that name in the folder. So we need to check whether the file is already there, and handle any errors should one of the folders be missing.

First let’s check whether the file is there, by adding these lines after ‘activate’:

if exists folder "Important Folder" of folder "Backup Folder" then

delete folder "Important Folder" of folder "Backup Folder"
end if

This will check if “Important Folder” exists in “Backup Folder”, and if it does, “Important Folder” will be moved into the Wastebasket.

Now for our error handling. We looked at the ‘try’ statement in the last part of this series, now let’s put it into practice. This goes on the line after ‘end if’:

try

copy folder "Important Folder" of folder ¬

"Original Folder" to folder "Backup Folder"
on error

move folder "Important Folder" of trash to folder "Backup Folder"

display dialog "The folder could not be copied!" buttons¬

 "Cancel" default button 1 with icon stop
end try

display dialog "The folder was copied successfully!" ¬

buttons "OK" default button 1 with icon note

Explained:

try

copy folder "Important Folder" of folder ¬

"Original Folder" to folder "Backup Folder"

First we try to copy the folder.

on error

if exists folder "Important Folder" of trash then

move folder "Important Folder" of trash to folder "Backup Folder"

end if

If the Finder encounters an error, then we first move the folder we put in the trash (if any) back to its original location.

display dialog "The folder could not be copied!" buttons¬

 "Cancel" default button 1 with icon stop

Then display a dialogue box with a ‘helpful’ message and a Cancel button. As we’ve discussed previously, a button called ‘Cancel’ will Cancel the script. If you don’t want the script to cancel when a Cancel button is pressed but spaces on both sides of the word ‘Cancel’ (you need only include one space before the word, but then the text doesn’t line up correctly on the button -- such fussiness!).

end try

End the try control statement.

display dialog "The folder was copied successfully!" ¬

buttons "OK" default button 1 with icon note

If the script is still running (i.e. if an error hasn’t been encountered and the script cancelled by the user clicking ‘Cancel’), display a dialogue box to say that the folder has been copied successfully. 

•
Run your script and you should see this:

You should also test that the script can handle an error. To test this we need to create a ‘problem’ for the script. 

•
Move the “Important Folder” out of the “Original Folder” and onto your desktop.

•
Run the script again. You should see this:

Also check that the backup of “Important Folder” was moved back from the wastebasket into “Backup Folder”. If so, the script has done its job perfectly.

References
The script works well, although it’s become a little untidy now. If we wanted to change the name of any of the folders, then we’d have a lot of editing to do. So instead we’re going to create variables to hold references to the folders we’ve created. By doing that we only need to edit the folder / file name in one place. Add these lines after ‘activate’:

set ItemName to "Important Folder"
set OriginalFolder to a reference to folder "Original Folder" of desktop
set BackupFolder to a reference to folder "Backup Folder" of desktop
set ItemToBackup to a reference to folder ItemName of OriginalFolder
set BackedUpItem to a reference to folder ItemName of BackupFolder
set TrashedBackedUpItem to a reference to ItemName of trash

Setting these variables with ‘a reference to’ tells AppleScript that they are references to objects (in this case folders).

•
So to use these new variables, we need to change the rest of the script to look like this:

if exists BackedUpItem then

delete BackedUpItem

end if

try

duplicate ItemToBackup to BackupFolder

on error

if exists TrashedBackedUpItem then

move TrashedBackedUpItem to BackupFolder

end if

display dialog "The folder could not be copied!" buttons ¬

"Cancel" default button 1 with icon stop

end try

display dialog "The folder was copied successfully!" ¬

buttons "OK" default button 1 with icon note
end tell

Notice that instead of the line that read ‘copy folder "Important Folder" of folder "Original Folder" to folder "Backup Folder"’, has been changed to ‘duplicate ItemToBackup to BackupFolder’. Normally, ‘duplicate’ and copy would achieve the same thing. The problem is that we’re now using references to objects, and when I tested the script with ‘copy’, for some reason the reference of ‘ItemToBackup’ was copied into the BackupFolder variable, rather than the files being copied in the Finder. Using ‘duplicate’ works around that. It’s a good job I test these scripts, eh?

•
Run the script again, and it should work no differently to before.

Long File Names
You may have noticed that files are referred to in a very long–winded (but user–friendly) way. A shortcut to typing these names out, should you be writing a script manually, is to use the ‘paste reference’ feature.

If you single-click on an icon in the Finder, and choose Copy from the Edit menu, then a reference to the object will be placed on the clipboard. If you look at the clipboard you will only see the object’s name. To use the file reference in Script Editor choose Paste Reference from the Edit menu. 

Processing Lots Of Files
As discussed in last month’s article (AppleScript Part 3), you can use loops to work your way through many items in a list. We won’t go into detail about those now, but here is an example ‘droplet’ script (a script that works when you drop items onto it) that calculates the total size of all the items dropped onto it:

on open theList

set totalSize to 0

tell application "Finder"

repeat with x in theList

set totalSize to (totalSize + (size of x))

end repeat

display dialog "The total size is: " & (totalSize as string) & " bytes"

end tell
end open

•
Cut and paste the above lines into a new script.

Let’s work through the script line by line:

on open theList
Anything in a script enclosed in ‘on open’ is only run when items are dropped onto a script, and it must be coded for a script to become a ‘droplet’. The variable ‘theList’ is a list of items (files and folders) dropped onto the script, and will look something like this: folder "Goodies" of folder "Adobe Photoshop" of startup disk, folder "Plug-ins" of folder "Adobe Photoshop" of startup disk, file "Adobe Photoshop® 4.0.1" of folder "Adobe Photoshop" of startup disk.

set totalSize to 0
Then we create a new variable called ‘totalSize’ and set its contents to 0.

tell application "Finder"
Self explanatory?

repeat with x in theList
This is the start of our loop. We repeat with a variable called ‘x’ in the list variable cryptically named ‘theList’. This is a special kind of loop for dealing with items in lists, which have been explained in previous parts of this series. The variable ‘x’ will refer to the current item (file or folder, in this case) in theList. All lines of script after this and until ‘end repeat’ will be performed in the loop.

set totalSize to (totalSize + (size of x))
Here we set the ‘totalSize’ variable to itself plus the size of ‘x’, the current item in theList.

end repeat
That’s all we need to do for each item in the list, so we end the loop with ‘end repeat’.

display dialog "The total size is: " & (totalSize as string) & " bytes"
Now display a result in a dialog box.

end tell
This stops sending instructions to the application specified in the ‘tell’ statement.

end open
This completes our ‘on open’ processing.

The script isn’t perfect, yet. It would probably be better if we show the size in kilobytes, provided the size is 1024 or more bytes. 

•
After ‘end repeat’ we can insert these lines:

if totalSize is greater than 1023 then -- we're talking KB

set totalSize to (totalSize / 1024) -- show in KB

set totalSize to (round totalSize rounding to nearest)

set sizeUnit to " KB"
else

set sizeUnit to " bytes"
end if

This will check whether ‘totalSize’ is greater than 1023 (a kilobyte or more), and if so, it will divide totalSize by 1024, to show the figure in kilobytes, and ‘round’ it to the nearest who number (the round command can be found in the ‘Numerics’ dictionary in the Scripting Additions folder). Then we create a variable called ‘sizeUnit’, set this to “ KB”.

Otherwise, if totalSize is less than or equal to 1023, we simply set ‘sizeUnit’ to “bytes”.

•
Change the ‘display dialog’ statement to:

display dialog "The total size is: " & (totalSize as string) & sizeUnit ¬

buttons "OK" default button 1 with icon note

so that ‘sizeUnit’ is shown at the end, so KB or bytes will be displayed as appropriate.

Helpful Droplets
The only thing that’s missing is a message for when the script is double-clicked. Again, this isn’t mandatory, but is more helpful than the droplet opening and immediately closing when it’s doubled clicked.

•
Put this code after ‘end open’:

on run

set myMsg to ¬

"This script calculates the total size of files and folders dropped onto it. " & return & return ¬

& "To use this script, drop some files onto its icon."

beep

display dialog myMsg buttons "Quit" default button 1 with icon stop
end run

The code is very simple, ‘on run’ -- when the user double-clicks -- create a variable called ‘myMsg’ and set its contents to a message informing the user how to use the script, then beep and show the message on the screen.

•
To test this droplet, save your script as an application on the desktop with a suitable name (such as ‘Calculate Size Of Items’), and double-click it. You should see your message:

This brings us nicely to the end of this part. We’ve written a script to back up a folder from one place to another in which we used ‘references’ so that it could be easily customised to use different files and error handling (the ‘try… on error… end try’ control statement) to deal with any problems and supply the user with a friendly-ish error message.

The second script we worked on was a ‘droplet’ that can calculate the total size of the items dropped onto it -- information the Finder cannot currently supply. It can work with any number of files because it uses a loop (‘repeat… end repeat’ control statement). It’s a helpful droplet too. If you double–click it, it’ll display a nice message informing you of its purpose and how it should be used.

Here’s a summary of technical terms used this month:

Until next month enjoy scripting!.
Comments to:
helpdesk@1984-online.com